#include "pch.h"
#include "BoundBox.h"
#include "Engine.h"
#include "ResourceManager.h"
#include "RendererEngine.h"
#include "UnitDerive.h"

namespace mini
{
	CShaderUnit* CBoundBox::gShaderUnitLine = nullptr;
	CMatrix CBoundBox::gWorldMatrix;
	bool  CBoundBox::gbCreateShader = false;
	float CBoundBox::gOffset = 0.0001f;
	CBoundBox::CBoundBox()
	: CBoundBox(CPoint3F(), CPoint3F())
	{
	}


	CBoundBox::CBoundBox(CPoint3F min, CPoint3F max)
	{
		mbDirty = true;
		mPt = CPoint3F(0, 0, 0);
		mRadius = CPoint3F(0, 0, 0);
		ZeroMemory(mBoundBoxPit, sizeof(mBoundBoxPit));
		initialiseBuffer();
		D3DXMatrixIdentity(&gWorldMatrix);
	}

	CBoundBox::~CBoundBox()
	{
	}

	void CBoundBox::setPosition(const CPoint3F& pt)
	{
		mbDirty = true;
		//mPt = mOriginalPt + pt;
		//for (auto& i : mBoundBoxPit)
		//{
		//	i.x += pt.x;
		//	i.y += pt.y;
		//	i.z += pt.z;
		//}
		//update();
	}

	void CBoundBox::setScale(const CPoint3F& scale)
	{
		mbDirty = true;
		//mRadius.x = mOriginalRadius.x * scale.x;
		//mRadius.y = mOriginalRadius.y * scale.y;
		//mRadius.z = mOriginalRadius.z * scale.z;
		//update();
	}

	void CBoundBox::setRotation(const CPoint3F& rotation)
	{
		mbDirty = true;
	}

	void CBoundBox::setRadius(const CPoint3F& radiuse)
	{
		mbDirty = true;
		//mRadius = radiuse;
		//update();
	}

	void CBoundBox::relateMeshUnit(CMeshUnit* pMeshUnit)
	{
		mpMeshUnit = pMeshUnit;
	}

	CPoint3F& CBoundBox::getCenterPt()
	{
		return mPt;
	}

	CPoint3F& CBoundBox::getRadius()
	{
		return mRadius;
	}

	const CPoint3F& CBoundBox::getMinPos() const
	{
		return mMinPos;
	}

	const CPoint3F& CBoundBox::getMaxPos() const
	{
		return mMaxPos;
	}

	const CPoint3F& CBoundBox::size() const
	{
		return mSize;
	}

	const CPoint3F& CBoundBox::getOriginalMinPos() const
	{
		return mOriginalMinPos;
	}

	const CPoint3F& CBoundBox::getOriginalMaxPos() const
	{
		return mOriginalMaxPos;
	}

	void CBoundBox::setVisible(bool bValue)
	{
		bValue ? show() : hide();
	}

	void CBoundBox::show()
	{
		mVisible = true;
	}

	void CBoundBox::hide()
	{
		mVisible = false;
	}

	bool CBoundBox::isShow()
	{
		return mVisible;
	}

	bool CBoundBox::checkPoint(const CPoint3F& p)
	{
		if ( p.x < mMinPos.x || p.x > mMaxPos.x)
		{
			return false;
		}
		if (p.y < mMinPos.y || p.y > mMaxPos.y)
		{
			return false;
		}
		if (p.z < mMinPos.z || p.z > mMaxPos.z)
		{
			return false;
		}
		return true;	
	}

	bool CBoundBox::checkBoundBox(const CBoundBox& boundbox)
	{
		if (mMinPos.x > boundbox.mMaxPos.x)
		{
			return false;
		}
		if (mMaxPos.x < boundbox.mMinPos.x)
		{
			return false;
		}
		
		if (mMinPos.y > boundbox.mMaxPos.y)
		{
			return false;
		}
		if (mMaxPos.y < boundbox.mMinPos.y)
		{
			return false;
		}

		if (mMinPos.z > boundbox.mMaxPos.z)
		{
			return false;
		}
		if (mMaxPos.z < boundbox.mMinPos.z)
		{
			return false;
		}
		return true;
	}



	bool CBoundBox::checkBoundBox(const CBoundBox& boundbox, CBoundBox* pBoundBox)
	{
		if(checkBoundBox(boundbox))
		{
			if (pBoundBox != nullptr)
			{
				pBoundBox->mMinPos.x = max(mMinPos.x, boundbox.mMinPos.x);
				pBoundBox->mMaxPos.x = min(mMaxPos.x, boundbox.mMaxPos.x);
				pBoundBox->mMinPos.y = max(mMinPos.y, boundbox.mMinPos.y);
				pBoundBox->mMaxPos.y = min(mMaxPos.y, boundbox.mMaxPos.y);
				pBoundBox->mMinPos.z = max(mMinPos.z, boundbox.mMinPos.z);
				pBoundBox->mMaxPos.z = min(mMaxPos.z, boundbox.mMaxPos.z);

			}
			return true;
		}
		return false;
	}

	void CBoundBox::reset()
	{
		mPt = CPoint3F(0, 0, 0);
		mMinPos = mPt;
		mMaxPos = mPt;
		mRadius = mPt;
		mOriginalPt = mPt;
		mOriginalMinPos = mPt;
		mOriginalMaxPos = mPt;
		mOriginalRadius = mPt;
		for (int i = 0; i < 8; ++i)
		{
			mBoundBoxPit[i] = mPt;
			mOriginalBoundBoxPit[i].x = mPt.x;
			mOriginalBoundBoxPit[i].y = mPt.y;
			mOriginalBoundBoxPit[i].z = mPt.z;
		}
	}

	void CBoundBox::draw(DeviceContext* pDeviceContext)
	{
		// Set vertex buffer stride and offset.
		UINT32 stride = sizeof(VertexFormatVC);
		UINT32 offset = 0;
		// Set the lines buffer to active in the input assembler so it can be rendered.
		pDeviceContext->IASetVertexBuffers(0, 1, &m_linesBuffer, &stride, &offset);

		// Set the type of primitive that should be rendered from this lines buffer, in this case lines.
		pDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINELIST);

		CRendererEngine::getSinglePtr()->setWorldMatrix(&gWorldMatrix);
		auto& matrices = CRendererEngine::getSinglePtr()->getTransposeMatrices();
		gShaderUnitLine->updateVSConstBuffer(0, &matrices[0]);

		D3D11_MAPPED_SUBRESOURCE mappedResource;
		// Lock the vertex buffer so it can be written to.
		HRESULT result = pDeviceContext->Map(m_linesBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
		if (FAILED(result))
		{
			return;
		}
		memcpy(mappedResource.pData, &m_lines[0], sizeof(VertexFormatVC) * mDrawLinePointCount);
		pDeviceContext->Unmap(m_linesBuffer, 0);

		pDeviceContext->Draw(mDrawLinePointCount, 0);

	}

	void CBoundBox::setBoundBoxRange(const CPoint3F& min, const CPoint3F& max)
	{
		mOriginalMinPos = min;
		mOriginalMaxPos = max;
		mMinPos = min;
		mMaxPos = max;

		mSize.x = fabs(max.x - min.x);
		mSize.y = fabs(max.y - min.y);
		mSize.z = fabs(max.z - min.z);

		mOriginalPt = (min + max) * 0.5f;
		mOriginalRadius = (max - min) * 0.5f;
		mRadius = mOriginalRadius;
		mPt = mOriginalPt;
		mRadiusLength = D3DXVec3Length(&CVector(&max.x));
		CPoint3F minPt = mPt - mRadius;
		CPoint3F maxPt = mPt + mRadius;
		mBoundBoxPit[0].x = min.x;
		mBoundBoxPit[0].y = max.y;
		mBoundBoxPit[0].z = min.z;

		mBoundBoxPit[1].x = max.x;
		mBoundBoxPit[1].y = max.y;
		mBoundBoxPit[1].z = min.z;

		mBoundBoxPit[2].x = max.x;
		mBoundBoxPit[2].y = max.y;
		mBoundBoxPit[2].z = max.z;

		mBoundBoxPit[3].x = min.x;
		mBoundBoxPit[3].y = max.y;
		mBoundBoxPit[3].z = max.z;

		mBoundBoxPit[4].x = min.x;
		mBoundBoxPit[4].y = min.y;
		mBoundBoxPit[4].z = min.z;

		mBoundBoxPit[5].x = max.x;
		mBoundBoxPit[5].y = min.y;
		mBoundBoxPit[5].z = min.z;

		mBoundBoxPit[6].x = max.x;
		mBoundBoxPit[6].y = min.y;
		mBoundBoxPit[6].z = max.z;

		mBoundBoxPit[7].x = min.x;
		mBoundBoxPit[7].y = min.y;
		mBoundBoxPit[7].z = max.z;

		memcpy(mOriginalBoundBoxPit, mBoundBoxPit, sizeof(mBoundBoxPit));
	}

	void CBoundBox::setBoundBoxRange(float fValue)
	{
		mMinPos -= fValue;
		mMaxPos += fValue;
		setBoundBoxRange(mMinPos, mMaxPos);
	}

	void CBoundBox::update(CMatrix& worldMatrix)
	{
		if (mbDirty)
		{
			D3DXVECTOR4 pos; 
			for (int i = 0; i < 8; ++i)
			{
				D3DXVec3Transform(&pos, &mOriginalBoundBoxPit[i], &worldMatrix);
				memcpy(&mBoundBoxPit[i], pos, sizeof(CPoint3F));
			}	
			fillLineData();
			mbDirty = false;
		}
	}

	void CBoundBox::editorPosition(const CPoint3F& p)
	{
		for( int i =0;i < 8; ++i )
		{
			mBoundBoxPit[i] += p;
		}
		mMinPos += p;
		mMaxPos += p;
		mPt += p;
	}

	bool CBoundBox::operator==(const CBoundBox& box)
	{
		return (fabs(mMinPos.x - box.mMinPos.x) <= 0.000001f &&
				fabs(mMinPos.y - box.mMinPos.y) <= 0.000001f &&
				fabs(mMinPos.z - box.mMinPos.z) <= 0.000001f &&
				fabs(mMaxPos.x - box.mMaxPos.x) <= 0.000001f &&
				fabs(mMaxPos.y - box.mMaxPos.y) <= 0.000001f &&
				fabs(mMaxPos.z - box.mMaxPos.z) <= 0.000001f );
	}

	bool CBoundBox::initialiseBuffer()
	{
		mDrawLinePointCount = 24;
		m_lines.resize(mDrawLinePointCount);

		for (auto& item : m_lines)
		{
			item.color = CPoint4F(1.0f, 1.0f, 1.0f, 1.0f);
		}
		fillLineData();

		std::vector<D3D11_INPUT_ELEMENT_DESC> input_element;
		input_element.resize(2);
		input_element[0].SemanticName = "POSITION";
		input_element[0].SemanticIndex = 0;
		input_element[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
		input_element[0].InputSlot = 0;
		input_element[0].AlignedByteOffset = 0;
		input_element[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
		input_element[0].InstanceDataStepRate = 0;

		input_element[1].SemanticName = "COLOR";
		input_element[1].SemanticIndex = 0;
		input_element[1].Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
		input_element[1].InputSlot = 0;
		input_element[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
		input_element[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
		input_element[1].InstanceDataStepRate = 0;

		// Set up the description of the dynamic vertex buffer.
		D3D11_BUFFER_DESC linesBufferDesc;
		linesBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
		linesBufferDesc.ByteWidth = sizeof(VertexFormatVC) * m_lines.size();
		linesBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
		linesBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
		linesBufferDesc.MiscFlags = 0;
		linesBufferDesc.StructureByteStride = 0;

		// Give the subresource structure a pointer to the vertex data.
		D3D11_SUBRESOURCE_DATA vertexData;
		vertexData.pSysMem = &m_lines[0];
		vertexData.SysMemPitch = 0;
		vertexData.SysMemSlicePitch = 0;
		
		auto pDevice = CEngine::getSinglePtr()->getDevice();
		
		
		auto pShader = dynamic_cast<CShader*>(CResourceManager::getSinglePtr()->createResource(Shader));
		
		pShader->setDevice(pDevice);
		gShaderUnitLine = new CShaderUnit(pShader);
		HRESULT result = pDevice->getDevice()->CreateBuffer(&linesBufferDesc, &vertexData, &m_linesBuffer);
		if (FAILED(result))
		{
			return false;
		}

		if (gbCreateShader)
		{
			if (!(pShader->createVextexShader("resource/Shader/renderLine.vs", "guiVertexShader", "vs_5_0", input_element) &&
				pShader->createPixelShader("resource/Shader/renderLine.ps", "guiPixelShader", "ps_5_0")))
			{
				return false;
			}
			auto matrices = CRendererEngine::getSinglePtr()->getTransposeMatrices();
			return gShaderUnitLine->addVSParam("", matrices.size() * sizeof(CMatrix), 0, 0);
		}
		else
		{
			return true;
		}
	}

	void CBoundBox::fillLineData()
	{
		m_lines[0].position = mBoundBoxPit[0];
		m_lines[1].position = mBoundBoxPit[1];

		m_lines[2].position = mBoundBoxPit[1];
		m_lines[3].position = mBoundBoxPit[2];

		m_lines[4].position = mBoundBoxPit[2];
		m_lines[5].position = mBoundBoxPit[3];

		m_lines[6].position = mBoundBoxPit[3];
		m_lines[7].position = mBoundBoxPit[0];

		m_lines[8].position = mBoundBoxPit[4];
		m_lines[9].position = mBoundBoxPit[5];

		m_lines[10].position = mBoundBoxPit[5];
		m_lines[11].position = mBoundBoxPit[6];

		m_lines[12].position = mBoundBoxPit[6];
		m_lines[13].position = mBoundBoxPit[7];

		m_lines[14].position = mBoundBoxPit[7];
		m_lines[15].position = mBoundBoxPit[4];

		m_lines[16].position = mBoundBoxPit[0];
		m_lines[17].position = mBoundBoxPit[4];

		m_lines[18].position = mBoundBoxPit[1];
		m_lines[19].position = mBoundBoxPit[5];

		m_lines[20].position = mBoundBoxPit[2];
		m_lines[21].position = mBoundBoxPit[6];

		m_lines[22].position = mBoundBoxPit[3];
		m_lines[23].position = mBoundBoxPit[7];
	}
}
